﻿using Common;
using Common.Jwt;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
using System.Runtime.CompilerServices;
using System.Text;

namespace ApiServer.Utilitys.AuthorizationExt
{

    /// <summary>
    /// jwt  
    /// 请求需要授权的api
    /// 1、如果是没有带token，带上了一个过期的token----401
    /// 2、token正确，token在有效期内，解析token后，经过计算，发现token对应的请求的api，没有权限---403
    /// </summary>
    public static class AuthorizationExt
    {

        /// <summary>
        /// 支持授权的扩展方法
        /// </summary>
        /// <param name="builder"></param>
        public static void UseAuthorizationExt(this WebApplicationBuilder builder)
        {
            JWTTokenOptions tokenOptions = new JWTTokenOptions();
            builder.Configuration.Bind("JWTTokenOptions", tokenOptions);

            builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)//Scheme
            .AddJwtBearer(options =>
            {
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    //JWT有一些默认的属性，就是给鉴权时就可以筛选了
                    ValidateIssuer = true,//是否验证Issuer
                    ValidateAudience = true,//是否验证Audience
                    ValidateLifetime = true,//是否验证失效时间
                    ValidateIssuerSigningKey = true,//是否验证SecurityKey
                    ValidAudience = tokenOptions.Audience,//
                    ValidIssuer = tokenOptions.Issuer,//Issuer，这两项和前面签发jwt的设置一致
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(tokenOptions.SecurityKey!)),
                    ClockSkew = TimeSpan.FromSeconds(0),//设置token过期后多久失效，默认过期后300秒内仍有效 
                };

                options.Events = new JwtBearerEvents
                {
                    //此处为权限验证失败后的执行的委托，涵盖的场景：
                    //1、没有token的
                    //2、token错误的
                    //3、Token过期的
                    OnChallenge = context =>
                    {
                        //此处代码为终止.Net Core默认的返回类型和数据结果，这个很重要哦，必须
                        context.HandleResponse();
                        //自定义自己想要返回的数据结果，我这里要返回的是Json对象，通过引用Newtonsoft.Json库进行转换
                        var payload = JsonConvert.SerializeObject(new ApiResultData<int>()
                        {
                            Success = false,
                            Message = "对不起没有授权，没有Token",
                            Data = 0,
                            OValue = 401
                        }, new JsonSerializerSettings
                        {
                            ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver()
                        });
                        //自定义返回的数据类型
                        context.Response.ContentType = "application/json";
                        //自定义返回状态码，默认为401 我这里改成 200
                        context.Response.StatusCode = StatusCodes.Status200OK;
                        //context.Response.StatusCode = StatusCodes.Status401Unauthorized;
                        //输出Json数据结果
                        context.Response.WriteAsync(payload);
                        return Task.FromResult(0);
                    },


                    //此处为权限校验失败后的执行的委托；涵盖的场景：
                    //1.带的有token，token是有效期内的，通过一些逻辑计算后，发现还是没有权限；
                    OnForbidden = context =>
                    {
                        //自定义自己想要返回的数据结果，我这里要返回的是Json对象，通过引用Newtonsoft.Json库进行转换
                        string payload = JsonConvert.SerializeObject(new ApiResultData<int>()
                        {
                            Success = false,
                            Message = "对不起，您不具备访问该功能的权限，token是对的，token也在有效期内",
                            Data = 1,
                            OValue = 403
                        }, new JsonSerializerSettings
                        {
                            ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver()
                        });


                        //自定义返回的数据类型
                        context.Response.ContentType = "application/json";
                        //自定义返回状态码，默认为403 我这里改成 200
                        context.Response.StatusCode = StatusCodes.Status200OK;
                        //context.Response.StatusCode = StatusCodes.Status403Forbidden;
                        //输出Json数据结果
                        context.Response.WriteAsync(payload);
                        return Task.FromResult(0);
                    },
                    //OnTokenValidated = context =>
                    //{
                    //    Console.WriteLine("Token解析成功了");
                    //    return Task.FromResult(0);
                    //},
                    //OnAuthenticationFailed = context =>
                    //{
                    //    Console.WriteLine("Token解析成功了");
                    //    return Task.FromResult(0);
                    //},
                    //OnMessageReceived = context =>
                    //{
                    //    return Task.FromResult(0);
                    //}

                };
            });

            //添加鉴权策略
            builder.Services.AddAuthorization(options =>
            {
                options.AddPolicy("btnPolicy", policyBuilder => policyBuilder
                .AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
                .AddRequirements(new MenuAuthorizeRequirement()));   //可以去可扩展 业务上需要的逻辑验证---验证失败403 
            });
            //注入
            builder.Services.AddTransient<IAuthorizationHandler, MenuAuthorizeHandler>();
        }
    }
}
